import java.io.OutputStream;
import java.util.Stack;

/**
 * 
 * @author Guillaume PANNETIER
 * @author Bertrand LE MAROUILLE
 * @author Quentin DORVEAUX
 * @author Corentin GUICHAOUA
 *
 * Cette classe cree le code assembleur a partir du code Yaka
 *
 */
public class YVMasm extends YVM {
	private int cptCh; //Compte le num�ro de la chaine � afficher (mess0, mess1, ...)
	private OutputStream fichier;
	
	/**
	 * Constructeur de la classe. Il initialise le compteur de chaine et le fichier.
	 */
	public YVMasm(){
		super();
		cptCh = 0;
		fichier = Ecriture.ouvrir("resultat.asm");
	}
	
	/**
	 * Affiche les entetes assembleur necessaires en debut de fichier
	 */
	public void entete(){
		printTab("extrn lirent:proc");
		printTab("extrn ecrent:proc");
		printTab("extrn ecrbool:proc");
		printTab("extrn ecrch:proc");
		printTab("extrn ligsuiv:proc");
		
		print(".model SMALL");
		print(".586");
		print("");
		
		print(".CODE");		
	}
	/**
	 * Affiche 	debut :
	 * 			STARTUPCODE
	 */
	public void ouvrePrinc(){
		print("debut :");
		printTab("STARTUPCODE");
	}
	/**
	 * Affiche "push word ptr" suivi de la valeur passee en parametre
	 * @valeur la valeur que l'on veut mettre dans la pile
	 */
	public void iconst(int valeur){
		printTab("push word ptr "+valeur);
	}
	/**
	 * Affiche le code assembleur necessaire pour faire une divison entre les 2 valeurs en sommet de pile
	 */
	public void idiv(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("cwd");
		printTab("idiv bx");
		printTab("push ax");
	}
	/**
	 * Affiche le code assembleur necessaire pour faire une addition entre les 2 valeurs en sommet de pile
	 */
	public void iadd(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("add ax,bx");
		printTab("push ax");
	}
	/**
	 * Affiche le code assembleur necessaire pour affecter a la variable correspondant a l'offset en parametre, la valeur en sommet de pile
	 * @offset l'offset correspondant a la variable a laquelle on veut affecter une valeur
	 */
	public void istore(int offset){
		printTab("pop ax");
		if(offset < 0)
			printTab("mov word ptr [bp"+offset+"], ax");
		else
			printTab("mov word ptr [bp+"+offset+"], ax");
	}
	/**
	 * Affiche le code assembleur necessaire pour mettre la valeur de la variable  correspondant a l'offset en parametre dans la pile
	 * @offset l'offset correspondant a la valeur de la variable que l'on veut mettre dans la pile
	 */
	public void iload(int offset){
		if(offset < 0)
			printTab("push word ptr [bp"+offset+"]");
		else
			printTab("push word ptr [bp+"+offset+"]");
	}
	/**
	 * Affiche le code assembleur necessaire pour faire une multiplication entre les 2 valeurs en sommet de pile
	 */
	public void imul(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("imul bx");
		printTab("push ax");
	}
	/**
	 * Affiche le code assembleur necessaire pour faire une soustraction entre les 2 valeurs en sommet de pile
	 */
	public void isub(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("sub ax,bx");
		printTab("push ax");
	}
	/**
	 * Affiche le code assembleur necessaire pour faire un OU logique entre les 2 valeurs en sommet de pile
	 */
	public void ior(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("or ax,bx");
		printTab("push ax");
	}
	/**
	 * Affiche le code assembleur necessaire pour faire un ET logique entre les 2 valeurs en sommet de pile
	 */
	public void iand(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("and ax,bx");
		printTab("push ax");
	}
	
	/**
	 * Affiche le code assembleur necessaire en fin de fichier
	 */
	
	public void queue(){
		printTab("nop");
		printTab("EXITCODE");
		printTab("end debut");
	}
	/**
	 * Affiche le code assembleur correspondant a l'ecriture d'un message sur la sortie standard
	 */
	public void ecrireChaine(String ch){
		ch=ch.substring(1, ch.length()-1);
		ch="\""+ch+"$\"";
		print(".DATA");
		//printTab("mess"+cptCh+" DB \""+ch+"$\"");
		printTab("mess" +cptCh+" DB "+ch);
		print(".CODE");
		printTab("lea dx, mess"+cptCh);
		printTab("push dx");
		printTab("call ecrch");
		cptCh++;
	}
	/**
	 * Affiche le code assembleur pour sauter a l'etiquette en parametre si la valeur en sommet de pile est a faux
	 * @ch l'etiquette ou l'onn veut aller si la valeur en sommet de pile est a faux
	 */
	public void iffaux(String ch){
		printTab("pop ax");
		printTab("cmp ax,0");
		printTab("je "+ch);
	}
	/**
	 * Affiche "jmp" suivi de l'etiquette en parametre
	 * @ch l'etiquette ou l'on veut aller
	 */
	public void goTo(String ch){
		printTab("jmp "+ch);
	}
	/**
	 * Affiche "call ligsuiv"
	 */
	public void aLaLigne(){
		printTab("call ligsuiv");
	}
	/**
	 * Affiche "call ecrEnt"
	 */
	public void ecrireEnt(){
		printTab("call ecrEnt");
	}
	
	public void lireEnt(){
		try {
			int ent;
			Ident id;
			id = Yaka.tabIdent.chercheIdent(YakaTokenManager.identLu);
			if(id.getClass().toString().equals("class IdVar"))
				ent = ((IdVar)id).getOffset();
			else
				ent = ((IdParam)id).getOffset();
			
			if(ent < 0)
				printTab("lea dx, [bp"+ent+"]");
			else
				printTab("lea dx, [bp+"+ent+"]");
			
			printTab("push dx");
			printTab("call lirent");
		} catch (TabIdentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/*public void callmin(){
		printTab("call min");
	}
	
	public void callsup(){
		printTab("call sup");
	}*/
	/**
	 * Affiche"call" suivi de la fonction que l'on veut appeler
	 * @fct la fonction que l'on veut appeler
	 */
	public void call(String fct){
		printTab("call "+fct);
	}
	/**
	 * Affiche "sub sp,2" afin de reserver la place pour le retour
	 */
	public void reserveRetour(){
		printTab("sub sp,2");
	}
	/**
	 * Affiche "enter *,0" suivant l'entier en parametre
	 * @ent l'entier correspondant au nombre de variables locales de la fonction
	 */
	public void ouvBloc(int ent){
		printTab("enter "+ent+",0");
	}
	/**
	 * Affiche les instructions de fin de fonction "leave" et "ret" suivant le nombre d'arguments de la fonction
	 */
	public void fermeBloc(){
		int ent;
		try {
			ent = 2*((IdFun)Yaka.tabIdent.chercheIdent(YakaTokenManager.fonctionLue)).getArgs().size();
			printTab("leave");
			printTab("ret "+ent);
		} catch (TabIdentException e){
			e.printStackTrace();
		}
	}
	/**
	 * Affiche le code assembleur pour affecter la valeur de retour
	 */
	public void ireturn(){
		int ent;
		try {
			ent = 4+2*((IdFun)Yaka.tabIdent.chercheIdent(YakaTokenManager.fonctionLue)).getArgs().size();
			printTab("pop ax");
			if(ent < 0)
				printTab("mov [bp"+ent+"], ax");
			else
				printTab("mov [bp+"+ent+"], ax");
		} catch (TabIdentException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Affiche le code assembleur pour inverser le signe de la valeur en sommet de pile
	 */
	 public void ineg(){
		if(correct){
			printTab("mov ax, 0");
			printTab("pop bx");
			printTab("sub ax,bx");
			printTab("push ax");
		}
	}
	
	/**
	 * TODO : g�rer cela en asm.
	 */
	/*public void inot(int bool){
		if(correct){
			if(bool == -1)
				printTab("")
		}
	}*/
	
	
	
	
	
	
	/*
	 * ************************
	 *      COMPARAISONS
	 * ************************
	 */
	/**
	 * Affiche le code assembleur correspondant a l'instruction "iinfegal"
	 */
	public void iinfegal(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("cmp ax, bx");
		printTab("jg $+6");
		printTab("push -1");
		printTab("jmp $+4");
		printTab("push 0");
	}
	/**
	 * Affiche le code assembleur correspondant a l'instruction "isup"
	 */
	public void isup(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("cmp ax, bx");
		printTab("jle $+6");
		printTab("push -1");
		printTab("jmp $+4");
		printTab("push 0");
	}
	/**
	 * Affiche le code assembleur correspondant a l'instruction "iinf"
	 */
	public void iinf(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("cmp ax, bx");
		printTab("jge $+6");
		printTab("push -1");
		printTab("jmp $+4");
		printTab("push 0");
	}
	/**
	 * Affiche le code assembleur correspondant a l'instruction "isupegal"
	 */
	public void isupegal(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("cmp ax, bx");
		printTab("jl $+6");
		printTab("push -1");
		printTab("jmp $+4");
		printTab("push 0");
	}
	/**
	 * Affiche le code assembleur correspondant a l'instruction "iegal"
	 */
	public void iegal(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("cmp ax, bx");
		printTab("jne $+6");
		printTab("push -1");
		printTab("jmp $+4");
		printTab("push 0");
	}
	/**
	 * Affiche le code assembleur correspondant a l'instruction "idiff"
	 */
	public void idiff(){
		printTab("pop bx");
		printTab("pop ax");
		printTab("cmp ax, bx");
		printTab("je $+6");
		printTab("push -1");
		printTab("jmp $+4");
		printTab("push 0");
	}
	
	
	/*
	 * *******************
	 *     AFFICHAGE
	 * *******************
	 */
	
	public void print(String message){
		//System.out.println(message);
		if(correct)
			Ecriture.ecrireString(fichier, message+"\n");
		
	}
	
	public void printTab(String message){
		//System.out.println("\t"+message);
		if(correct)
			Ecriture.ecrireString(fichier, "\t"+message+"\n");
	}
}
